package cn.itcast.hotel.service.impl;

import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.pojo.PageResult;
import cn.itcast.hotel.pojo.RequestParams;
import cn.itcast.hotel.service.IEsHotelService;
import com.alibaba.fastjson.JSON;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Service
public class IEsHotelServiceImplRestClient implements IEsHotelService {

    // 需要在启动类，添加@Bean，注入到IOC容器
    @Autowired
    private RestHighLevelClient client;

    @Override
    public PageResult search(RequestParams params) {
        // 使用RestClient语法，操作ES,获取搜索结果的数据
        // 1. 创建请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");
        // 2. 组织DSL语句，基于关键字查询
        QueryBuilder queryBuilder = null;
        // 全文检索
        queryBuilder = buildBasicQuery(params);
        searchRequest.source().query(queryBuilder);
        // 高亮
        searchRequest.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));

        // 分页
        searchRequest.source().from((params.getPage()-1)*params.getSize()).size(params.getSize());
        // 3. 发送请求
        try {
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            // 4.处理结果集
            PageResult pageResult = handleResponse(response);
            return pageResult;
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 默认返回
        PageResult pageResult = new PageResult();
        pageResult.setTotal(0L);
        pageResult.setHotels(Collections.emptyList());
        return pageResult;
    }

    // 构建查询条件
    private QueryBuilder buildBasicQuery(RequestParams params) {
        BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
        // 关键字,必须条件
        String key = params.getKey();
        if (key == null || "".equals(key)){
            boolBuilder.must(QueryBuilders.matchAllQuery());
        }else{
            boolBuilder.must(QueryBuilders.matchQuery("all",key));
        }
        // 城市条件
        if(params.getCity() != null){
            boolBuilder.filter(QueryBuilders.termQuery("city",params.getCity()));
        }
        // 品牌条件
        if(params.getBrand() != null){
            boolBuilder.filter(QueryBuilders.termQuery("brand",params.getBrand()));
        }

        // 添加算法函数
        FunctionScoreQueryBuilder funBuilder = QueryBuilders.functionScoreQuery(boolBuilder, new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                        // 条件
                        QueryBuilders.termQuery("isAD", true),
                        // 算法函数
                        ScoreFunctionBuilders.weightFactorFunction(10)
                )
        });
        // 加权方式
        funBuilder.boostMode(CombineFunction.SUM);
        return funBuilder;
    }

    private PageResult handleResponse(SearchResponse response) {
        SearchHits searchHits = response.getHits();
        long value = searchHits.getTotalHits().value;
        System.out.println("记录数:"+value);
        // 4.2 获取原始数据,每条记录就是一个SearchHit对象
        SearchHit[] hits = searchHits.getHits();
        List<HotelDoc> hotelDocList = new ArrayList<>();
        for (SearchHit searchHit:hits){
            // 获取原始数据
            // 封装到HotelDoc对象
            HotelDoc hotelDoc = JSON.parseObject(searchHit.getSourceAsString(), HotelDoc.class);
            // 处理高亮字段
            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            // 判断集合是否为空
            if (!CollectionUtils.isEmpty(highlightFields)){
                // 获取name
                HighlightField highlightField = highlightFields.get("name");
                if (highlightField!=null){
                    String highName = highlightField.getFragments()[0].toString();
                    // 替换原始的名字
                    hotelDoc.setName(highName);
                }
            }
            hotelDocList.add(hotelDoc);
        }
        PageResult pageResult = new PageResult();
        pageResult.setTotal(value);
        pageResult.setHotels(hotelDocList);
        return pageResult;
    }
}
